<?php
class ZeusLib_Resource_Xml_Collection_Element extends ZeusLib_Object
{

	/**
	 * Parent object variable
	 *
	 * @var ZeusLib_Resource_Xml_Collection_Element
	 */
	private $parent;

	/**
	 * Tagname of the element
	 * 
	 * @var string
	 */
	private $name;

	/**
	 * Attributes array with strings as the key and
	 * values as the values for the attributes
	 * 
	 * @var array
	 */
	private $attributes;
	
	/**
	 * Value attribute to store the text content of the
	 * element in
	 * 
	 * @var mixed
	 */
	private $value;


	public function _construct()
	{
		// Initialize some stuff
		$this->attributes = array();
	}

	/**
	 * Sets the parent object.
	 *
	 * @param ZeusLib_Resource_Xml_Collection_Element $parent
	 */
	public function setParent(ZeusLib_Resource_Xml_Collection_Element $parent)
	{
		// Set parent element
		$this->parent = $parent;
	}

	/**
	 * Gets the parent object.
	 *
	 * @return ZeusLib_Resource_Xml_Collection_Element
	 */
	public function getParent()
	{
		// Return parent element
		return $this->parent;
	}
	
	/**
	 * Sets the name of the element.
	 * 
	 * @param string $name
	 */
	public function setName($name)
	{
		// Set the name
		$this->name = $name;
	}
	
	/**
	 * Gets the name of the element.
	 * 
	 * @param string $name
	 */
	public function getName()
	{
		// Return the name
		return $this->name;
	}
	
	/**
	 * Sets an attribute for the element.
	 * 
	 * @param string $name
	 */
	public function setAttribute($attribute, $value)
	{
		// Set the attribute
		$this->attributes[$attribute] = $value;
	}
	
	/**
	 * Gets an attribute for the element.
	 * 
	 * @param string $name
	 */
	public function getAttribute($attribute)
	{
		// TODO casting?
		
		// Check whether the attibute exists
		if(isset($this->attributes[$attribute]))
		{
			// Return the attribute value
			return $this->attributes[$attribute];
		}
		
		// Return null
		return null;
	}
	
	/**
	 * Sets the value of the element.
	 * 
	 * @param string $name
	 */
	public function setValue($value)
	{
		// Set the value
		$this->value = $value;
	}
	
	/**
	 * Gets the value of the element.
	 * 
	 * @param string $name
	 */
	public function getValue()
	{
		// Return the value
		return $this->value;
	}
	
	/**
	 * Gets the value of the element as boolean.
	 * 
	 * @return boolean
	 */
	public function asBoolean()
	{
		// Return boolean value
		return $this->getValue() == 'true';
	}
	
	/**
	 * Gets the value of the element as integer.
	 *
	 * @return boolean
	 */
	public function asInt()
	{
		// Return integer value
		return $this->getValue() + 0;
	}

	/**
	 * Adds a child.
	 *
	 * @param ZeusLib_Resource_Xml_Collection_Element $child
	 */
	public function addChild(ZeusLib_Resource_Xml_Collection_Element $child)
	{
		// Set the child as data from this object
		$this->setData($child->getName(), $child);
	}
	
	/**
	 * Searches in the xml element structure for the slash separated
	 * nodes in the search string.
	 *
	 * E.g. find('config/global')
	 * within the config is a root element of the xml structure
	 * and global is a direct sub element of config.
	 *
	 * If the node does not exist, null is returned.
	 *
	 * @param string $search
	 * @return ZeusLib_Resource_Xml_Collection_Element|null
	 */
	public function find($search)
	{
		// Check whether the search is null so we have to return this element
		if($search === null)
		{
			// Return this object
			return $this;
		}

		// Get the position of the first slash
		$slashPos = strpos($search, '/');

		// Check whether a slash in the search string existed
		if($slashPos !== false)
		{
			// Set the requested name to the search string until the first slash
			$requestedName = substr($search, 0, $slashPos);
			
			// Set the next search string to the search begginning from the first slash
			$nextSearch = substr($search, $slashPos + 1);
		}
		// No slash in the search string
		else
		{
			// Set the requested name to this search
			$requestedName = $search;
			
			// Set the next search to null
			$nextSearch = null;
		}

		// Return the child if it exists, otherwise return null if the child cannot be found
		return $this->hasData($requestedName) ? $this->getData($requestedName)->find($nextSearch) : null;
	}

	/**
	 * Merges two elements.
	 *
	 * @param ZeusLib_Resource_Xml_Collection_Element $element
	 */
	public function merge(ZeusLib_Resource_Xml_Collection_Element $element)
	{
		// Check whether the element names are equal
		if($this->getName() == $element->getName())
		{
			// TODO: merge the attributes?


			// Iterate all element children to merge
			foreach($element as $elementChild)
			{
				// Check whether no children in this node with the element childs name exists
				if(!$this->hasData($elementChild->getName()))
				{
					// Add to this node the element child
					$this->addChild($elementChild);
				}
			}

			// Iterate all node children to continue recursion
			foreach($this as $thisChild)
			{
				// Get the element child for this child
				$elementChild = $element->getData($thisChild->getName());

				// Check whether the element child exists
				if($elementChild !== null)
				{
					// Start the recursion for the childs
					$thisChild->merge($elementChild);
				}
			}
		}
	}

	/**
	 * Output the xml element structure.
	 */
	public function toString($level = 0)
	{
		// Create an output buffer
		$output = str_repeat(' ', $level * 4).$this->getName()."\n";

		// Iterate all children
		foreach($this as $child)
		{
			// Add the output
			$output .= $child->toString($level + 1);
		}

		// Return the output
		return $output;
	}

}